package com.spica.platform.websocket.interceptor;

import com.spica.platform.base.model.SysUser;
import com.spica.platform.common.constant.CommonConstant;
import com.spica.platform.uaa.client.util.AuthUtils;
import com.spica.platform.websocket.util.SocketSessionRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.security.Principal;
import java.util.LinkedList;
import java.util.Map;

/**
 * 功能:频道拦截器,类似管道,获取消息的一些meta数据
 * 客户端入站通道拦截器:
 * 客户端入站拦截器可拦截：客户端连接请求(StompCommand.CONNECT)，客户端订阅请求(StompCommand.SUBSCRIBE)，客户端发送请求(StompCommand.SEND)
 */
@SuppressWarnings("all")
@Component
public class SocketChannelInterceptor implements ChannelInterceptor {

	@Autowired
	private SocketSessionRegistry webAgentSessionRegistry;

	/**
	 * 实际消息发送到频道之前调用
	 */
	@Override
	public Message<?> preSend(Message<?> message, MessageChannel channel) {
		System.out.println("preSend进入");
		StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
		if (StompCommand.CONNECT.equals(accessor.getCommand())) {
			String token = accessor.getFirstNativeHeader(CommonConstant.ACCESS_TOKEN);
			if (token == null) {
				throw new IllegalArgumentException("抱歉，您没有访问权限");
			}
			String username = AuthUtils.checkAccessToken(token).getUsername();
			if (username != null) {
				accessor.setUser(SecurityContextHolder.getContext().getAuthentication());
			} else {
				throw new IllegalArgumentException("抱歉，您没有访问权限");
			}
			//只在连接时，登记当前用户的连接
			Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
			if (raw instanceof Map) {
				Object name = ((Map) raw).get("name");
				if (name instanceof LinkedList) {
					String id = ((LinkedList) name).get(0).toString();
					String sessionId = accessor.getSessionId();
					// 统计用户在线数，可通过redis来实现更好
					webAgentSessionRegistry.registerSessionId(id, sessionId);
				}
			}
		} else if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())) {
			String topic = accessor.getDestination().toString();
			Authentication authentication = (Authentication) accessor.getUser();

			boolean result = checkPermission(authentication, topic);

			if (!result) {
				// 这里返回一个自定义提示消息，由于没有指令和相关参数，所以不会真正执行订阅
				Message<String> msg = new Message<String>() {
					@Override
					public String getPayload() {
						return "主题订阅失败";
					}
					@Override
					public MessageHeaders getHeaders() {
						return null;
					}
				};
				return msg;
				// 如果抛出异常，则会导致前端面页连接也会断开，不断重连
				// throw new IllegalArgumentException("抱歉，没有权限订阅该主题");
			}
		} else if (StompCommand.SEND.equals(accessor.getCommand())) {
			String topic = accessor.getDestination().toString();
			Authentication authentication = (Authentication) accessor.getUser();
			boolean result = checkPermission(authentication, topic);

			if (!result) {
				throw new IllegalArgumentException("抱歉，没有权限发送消息到该主题");
			}
		} else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) {
			//点击断开连接，这里会执行两次，第二次执行的时候，message.getHeaders.size()=5,第一次是6。直接关闭浏览器，只会执行一次，size是5。
			System.out.println("断开连接");
			//  如果同时发生两个连接，只有都断开才能叫做不在线
			Principal user = accessor.getUser();
			if (message.getHeaders().size() == 5 && user.getName() != null) {
				String sessionId = accessor.getSessionId();
				webAgentSessionRegistry.unregisterSessionId(user.getName(), sessionId);
			}
		}
		return message;
	}

	/**
	 * 验证当前用户是否有权限订阅主题
	 */
	private boolean checkPermission(Authentication authentication, String topic) {

		SysUser user = AuthUtils.getUser(authentication) ;

		return true;
	}

}
